#!/usr/bin/env python
#==============================================================================
#
#  $Id$
#
"""
    Test of 1000 random operations on the Full back-end.
"""
#
#   Copyright (C) 2006 Michael A. Muller
#
#   This file is part of ODB.
#
#   ODB is free software: you can redistribute it and/or modify it under the 
#   terms of the GNU Lesser General Public License as published by the Free 
#   Software Foundation, either version 3 of the License, or (at your option) 
#   any later version.
#
#   ODB is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU Lesser General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public License 
#   along with ODB.  If not, see <http://www.gnu.org/licenses/>.
#
#==============================================================================

import fixpath
from odb.full import FullEnv
import shutil, os
from utils import SortedList
import random

# change this to get too much information
verbose = False

# indicates whether to do an integrity check for every iteration
checkEveryIteration = False

def makeRandomString():
    size = random.randrange(0, 50)
    val = ''
    while len(val) < size:
        val += `random.random()`
    
    return val[:size]
      
print 'this is a test of 1000 random actions performed against a "Full" ' \
      'back-end.'

if not verbose:
    print 'running in quite mode'

# pre-setup
if os.path.exists('odb_test_db'):
    shutil.rmtree('odb_test_db')

def testBTree():
    
    def getEnvDB(env = None):
        if env: env.close()
        env = FullEnv('odb_test_db')
        db = env.getBTreeDB('foo')
        return env, db
   
    try:
        env, db = getEnvDB()
        db['hello'] = 'first'
        db['world'] = 'world'
        db.setWritable(True)
        env.checkpoint()
        db.checkIntegrity()
    
        env, db = getEnvDB(env)
        if verbose: db.dump()
        assert db['hello'] == 'first'
        assert db['world'] == 'world'
        
        db['last'] = 'the last'
        env.checkpoint()
    
        env, db = getEnvDB(env)
        assert db['last'] == 'the last'
    
        del db['last']
        env.checkpoint()
        
        env, db = getEnvDB(env)
        assert db.get('last') is None
        
        # verify that we can checkpoint a database that is fully stubbed (had a 
        # problem with this)
        env, db = getEnvDB(env)
        env.checkpoint()
        
        # empty the database
        if verbose: print 'deleting fields'
        del db['world']
        del db['hello']
        if verbose: print 'checkpointing empty db'
        env.checkpoint()
        env.checkIntegrity()
        if verbose: print 'done'
        
        env, db = getEnvDB(env)
        assert db.get('world') is None
        assert db.get('hello') is None
    
        random.seed('spug r0kz!')
        backup = SortedList()
    
        for i in xrange(1000):
            if verbose: print '=== iteration %d' % i
            action = random.randrange(0, 4)
            if action == 0:
            
                # pick a unique random key
                key = `random.random()`
                while db.has_key(key):
                    key = `random.random()`
                
                # assign it to a value
                val = random.random()
                if verbose: print '  adding %s = %s' % (key, val)
                db[key] = val
                backup.add(key, val)
    
            elif action == 1:
                if verbose: print '  checkpointing'
                env.checkpoint()
                env, db = getEnvDB(env)
                if not checkEveryIteration: env.checkIntegrity()
                
                # verify the entire database
                cur = db.cursor()
                for key, val in backup:
                    dbKey, dbVal = cur.next()
                    assert dbKey == key and dbVal == val
                
                cur.close()
    
            elif action == 2 and backup:
                key, val = random.choice(backup)
                if verbose: print '  deleting %s' % key
                
                # first, make sure that the database has the same value
                assert db[key] == val
                
                # and then delete it
                del db[key]
                backup.remove(key)
    
            elif action == 3 and backup:
                key, val = random.choice(backup)
                newVal = makeRandomString()
                if verbose: print '  replacing %s with %s' % (key, newVal)
                
                # sanity check...
                assert db[key] == val
                
                # replace
                db[key] = newVal
                backup.replace(key, newVal)
    
            # verify the entire database
            cur = db.cursor()
            for key, val in backup:
                dbKey, dbVal = cur.next()
                assert dbKey == key and dbVal == val
    
            cur.close()
    
            if checkEveryIteration:
                if verbose: print '=== checkpointing (checkEveryIteration = True)'
                env.checkpoint()
                if verbose: print '=== checking integrity'
                env.checkIntegrity()
        
        print 'Done.  All is well.'
    
    finally:
        if os.path.exists('odb_test_db'): shutil.rmtree('odb_test_db')

def testBList():
    def getEnvDB():
        if env: env.close()
        env = FullEnv('odb_test_db')
        db = env.getBListDB('foo')
        return env, db

    env, db = getEnvDB()

testBTree()
#testBList()
